Skip to content

Enable RVV-optimized TRSM kernels for RISCV64_ZVL128B#5830

Draft
Felix-Gong wants to merge 1 commit into
OpenMathLib:developfrom
Felix-Gong:feature/rvv-trsm-zvl128b
Draft

Enable RVV-optimized TRSM kernels for RISCV64_ZVL128B#5830
Felix-Gong wants to merge 1 commit into
OpenMathLib:developfrom
Felix-Gong:feature/rvv-trsm-zvl128b

Conversation

@Felix-Gong

@Felix-Gong Felix-Gong commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Enable RVV-optimized TRSM kernels (trsm_kernel_{LN,LT,RN,RT}_rvv_v1.c) for RISCV64_ZVL128B target
  • These RVV kernel implementations already exist in the repository and are used by the x280 target, but were not enabled for ZVL128B configuration
  • Affects all four TRSM variants (S/D/C/Z) in both left/right and normal/transposed configurations

Note on ZVL256B

ZVL256B is intentionally excluded from this change. Testing revealed that the RVV TRSM kernel produces incorrect results with VLEN=256 (e.g., 3x3 lower triangular solve returns wrong values). This needs to be investigated separately.

Test plan

  • Built and tested on RISC-V hardware with RVV 1.0 (VLEN=128)
  • DTRSM correctness verified (n=2, n=100)
  • STRSM correctness verified
  • CI tests pass

Use existing RVV-optimized TRSM kernel implementations instead of the
generic C versions for the RISCV64_ZVL128B target. The RVV kernels
(trsm_kernel_{LN,LT,RN,RT}_rvv_v1.c) are already present in the
repository and used by the x280 target, but were not enabled for
ZVL128B.

Note: ZVL256B is not included because the RVV TRSM kernel has
correctness issues with VLEN=256.

Signed-off-by: Felix-Gong <gongxiaofei24@iscas.ac.cn>
@Felix-Gong
Felix-Gong force-pushed the feature/rvv-trsm-zvl128b branch from c50f0a6 to 4fb51e3 Compare June 7, 2026 03:43
@Felix-Gong Felix-Gong changed the title Enable RVV-optimized TRSM kernels for RISCV64_ZVL128B and ZVL256B Enable RVV-optimized TRSM kernels for RISCV64_ZVL128B Jun 7, 2026
@Felix-Gong Felix-Gong closed this Jun 7, 2026
@Felix-Gong Felix-Gong reopened this Jun 7, 2026
@Felix-Gong
Felix-Gong marked this pull request as draft June 7, 2026 08:13
@hmeiland

Copy link
Copy Markdown
Contributor

Root-cause analysis of the RISCV64_ZVL128B CI failure (cblas_ctrsm FAILED / TESTS ABANDONED)

I dug into why this PR fails the TEST (RISCV64_ZVL128B ...) job. Sharing the findings so we can pick the right fix.

TL;DR

The trsm_kernel_*_rvv_v1.c kernels are only correct when the whole GEMM path is the VLEN-adaptive _rvv_v1 family (as on x280). RISCV64_ZVL128B keeps fixed-unroll GEMM/TRMM microkernels and fixed-width rectangular packing, so switching only the TRSM kernel line creates a packing-width mismatch → wrong TRSM results.

The packing contract mismatch

trsm_kernel_*_rvv_v1.c uses LMUL m2 and blocks the M dimension by the runtime VSETVL_MAX (= VLEN·2 / SEW):

size_t vl = VSETVL_MAX;      // M-block width
i = vl;
while (i <= m) {
    GEMM_KERNEL(vl, GEMM_UNROLL_N, kk, ...);   // reads packed A in vl-row blocks
    solve(vl, GEMM_UNROLL_N, aa + kk*vl*COMPSIZE, ...);
    aa += vl * k * COMPSIZE; kk += vl; i += vl;
}

The paired trsm_*copy_rvv_v1.c packs the triangular block in the same VSETVL_MAX-wide strips, so the kernel + its trsm-copy are internally self-consistent at any VLEN. But the rectangular part of A is packed by GEMM_ITCOPY, which on ZVL128B is the fixed-width ../generic/gemm_tcopy_$(UNROLL_M).c (block width = compile-time GEMM_UNROLL_M). The solver reads it back stepping by VSETVL_MAX. When VSETVL_MAX != GEMM_UNROLL_M, the strides disagree → incorrect results.

Why it fails on the CI runner (VLEN = 256, SpaceMiT X60)

VSETVL_MAX for LMUL m2 at VLEN=256 vs the ZVL128B param.h GEMM_UNROLL_M:

type SEW VSETVL_MAX (solver M-block) GEMM_UNROLL_M (packer) match?
S 32 16 8
D 64 8 8
C 32 16 8 ✗ (→ cblas_ctrsm FAILED)
Z 64 8 4

This lines up exactly with the observed cblas_ctrsm failure, and it is the same effect as the "wrong results at VLEN=256" that motivated excluding ZVL256B — it's one root cause, not two. x280 is unaffected because its GEMM kernel and copies are all _rvv_v1, so the rectangular block width is also VSETVL_MAX and everything agrees.

Note the mismatch depends on the runtime VLEN, not the build target name: a ZVL128B binary run on a VLEN≥256 core still uses VSETVL_MAX≥16, so it breaks even though the target says 128.

Options

  1. Revert TRSM to generic for this target (safe, guaranteed green, drops the RVV TRSM speedup):
    [SDCZ]TRSMKERNEL_{LN,LT,RN,RT} = ../generic/trsm_kernel_{LN,LT,RN,RT}.c
  2. Keep the optimization by making the whole ZVL128B GEMM path adaptive _rvv_v1 (like x280), or by writing TRSM kernels that block by GEMM_UNROLL_M instead of VSETVL_MAX. This is a larger change and needs on-hardware verification that GEMM/TRMM don't regress.

I'm planning to verify both empirically on a SpaceMiT X60 (VLEN=256) — reproduce the ctrsm failure as-is, then confirm the chosen fix. Happy to push the fix once validated. Let me know which direction you'd prefer.

@hmeiland

Copy link
Copy Markdown
Contributor

Hardware test results on X60 (VLEN=256, GCC 14.3.0)

I ran this PR through the ctest suite on real RISC-V hardware. The current PR failscblas_strsm returns wrong results (RESULT LESS THAN HALF ACCURATE, ctest exits non-zero).

Root cause: the trsm_kernel_*_rvv_v1.c kernels are coupled to the full x280-style RVV level-3 stack — they pack A/B in the VLEN-agnostic m2 layout and call GEMM_KERNEL internally, expecting the rvv_v1 GEMM kernel + rvv_v1 copies + x280 UNROLL/P/Q/R params. But RISCV64_ZVL128B keeps its own 8-wide GEMM micro-kernel and params, so the packed-panel layout the TRSM kernel produces doesn't match what the GEMM kernel consumes → incorrect results.

I verified this isn't just missing copy files: adding the TRSM*COPY_*=*_rvv_v1.c lines makes SYMM/HEMM pass but strsm still fails. Grafting the whole x280 level-3 stack with only partial params instead corrupts unrelated level-1 routines (SROTM), because the UNROLL/P/Q/R defines form one contract that must be migrated together.

Suggested path for this PR: revert the 16 [SDCZ]TRSMKERNEL_* assignments in kernel/riscv64/KERNEL.RISCV64_ZVL128B to the generic C kernels (../generic/trsm_kernel_*.c). With that change the full cblas suite passes on X60:

cblas_strsm  PASSED THE TESTS OF ERROR-EXITS
cblas_strsm  PASSED THE COLUMN-MAJOR COMPUTATIONAL TESTS (3528 CALLS)
cblas_strsm  PASSED THE ROW-MAJOR    COMPUTATIONAL TESTS (3528 CALLS)
cblas_dtrsm  PASSED ... (3528 CALLS, both major orders)
cblas_ctrsm  PASSED ... (2592 CALLS, both major orders)
cblas_ztrsm  PASSED ... (3528 CALLS, both major orders)

(CTEST_RC=0, zero failures across the suite.)

Optimized RVV TRSM on ZVL128B is worthwhile, but it is a complete level-3 ecosystem migration (self-consistent rvv_v1 GEMM + copies + params, matching the x280 target) rather than a 16-line kernel swap — better as dedicated follow-up work with its own hardware validation than folded into this PR.

@hmeiland

Copy link
Copy Markdown
Contributor

On-hardware validation on X60 (ZVL128B): current kernels are VLEN-dependent and corrupt on non-x280 targets — fix + evidence

Tested this PR on real RISC-V hardware (SpaceMiT X60, VLENB=32 / VLEN=256, 8 cores, EESSI GCC 14.3.0), building the RISCV64_ZVL128B target with S/D/C/Z TRSM wired to the _rvv_v1 kernels.

Root cause

The four _rvv_v1 kernels tile packed-A along M by the runtime VSETVL_MAX (= 16 for fp32 m2 on X60), but the GEMM packing routine they interoperate with packs by the compile-time GEMM_UNROLL_M (= 8 on ZVL128B). When VSETVL_MAX != GEMM_UNROLL_M the diagonal-block stride assumed by solve() no longer matches the packed layout, so the solve reads/writes the wrong blocks. On x280 the two happen to coincide (16 == 16), which is why CI on x280 looks fine — but the kernel is not actually VLEN-agnostic.

Evidence (original vs fixed, same harness)

Residual test ‖op(A)·X − B‖ / ‖X·op(A) − B‖ over m,n ∈ {1,2,4,7,8,9,10,12,15,23} × every Side/Uplo/Trans/Diag.

DTRSM on ZVL128B:

Kernel Cases Fails Worst residual
Current PR (VSETVL_MAX tiling) 1600 408 0.89 (total corruption)
Fix below (GEMM_UNROLL_M tiling) 1600 0 3.3e-16

Failures cluster exactly where m/n grow past the tile boundary, matching the VL-vs-UNROLL divergence.

Full fixed-kernel validation, all green on ZVL128B (8000 cases, 0 fails):

Routine Cases Fails Worst residual
STRSM 1600 0 1.4e-7
DTRSM 1600 0 3.3e-16
CTRSM (incl. ConjTrans) 2400 0 3.0e-7
ZTRSM (incl. ConjTrans) 2400 0 5.5e-16

Fix

Keep the RVV-vectorized solve() as-is; replace only the outer control flow so packed-A is tiled by GEMM_UNROLL_M (with a power-of-2 M remainder per N-block), matching the generic kernel/generic/trsm_kernel_{LN,LT,RN,RT}.c drivers. This makes the kernels genuinely VLEN-agnostic — correct on both ZVL128B (unroll 8) and x280 (unroll 16).

A patch against all four trsm_kernel_{LN,LT,RN,RT}_rvv_v1.c is attached below. Happy to open a PR against this branch if that's easier.

Validated on SpaceMiT X60 hardware.

Patch: VLEN-agnostic outer control for trsm_kernel_{LN,LT,RN,RT}_rvv_v1.c
diff --git a/kernel/riscv64/trsm_kernel_LN_rvv_v1.c b/kernel/riscv64/trsm_kernel_LN_rvv_v1.c
index 869561fb3..fcdd4f3b9 100644
--- a/kernel/riscv64/trsm_kernel_LN_rvv_v1.c
+++ b/kernel/riscv64/trsm_kernel_LN_rvv_v1.c
@@ -71,6 +71,30 @@ static FLOAT dm1 = -1.;
 #define GEMM_KERNEL   GEMM_KERNEL_N
 #endif
 
+#if GEMM_DEFAULT_UNROLL_M == 1
+#define GEMM_UNROLL_M_SHIFT 0
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 2
+#define GEMM_UNROLL_M_SHIFT 1
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 4
+#define GEMM_UNROLL_M_SHIFT 2
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 6
+#define GEMM_UNROLL_M_SHIFT 2
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 8
+#define GEMM_UNROLL_M_SHIFT 3
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 16
+#define GEMM_UNROLL_M_SHIFT 4
+#endif
+
 #if GEMM_DEFAULT_UNROLL_N == 1
 #define GEMM_UNROLL_N_SHIFT 0
 #endif
@@ -91,7 +115,9 @@ static FLOAT dm1 = -1.;
 #define GEMM_UNROLL_N_SHIFT 4
 #endif
 
-// Optimizes the implementation in ../arm64/trsm_kernel_LN_sve.c
+// Packed A is tiled by GEMM_UNROLL_M (the GEMM micro-kernel / itcopy contract),
+// NOT by runtime VL: tiling by VSETVL_MAX only matches x280 and silently
+// corrupts on other targets (e.g. zvl128b, unroll 8). Only solve() vectorizes.
 
 #ifndef COMPLEX
 
@@ -217,10 +243,6 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k,  FLOAT dummy1,
   BLASLONG i, j;
   FLOAT *aa, *cc;
   BLASLONG  kk;
-  
-  size_t vl = VSETVL_MAX;
-
-    //fprintf(stderr, "%s , %s, m = %4ld  n = %4ld  k = %4ld offset = %4ld\n", __FILE__, __FUNCTION__, m, n, k, offset); // Debug
 
   j = (n >> GEMM_UNROLL_N_SHIFT);
 
@@ -228,63 +250,62 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k,  FLOAT dummy1,
 
     kk = m + offset;
 
-    i = m % vl;
-    if (i) {
-      aa = a + (m - i) * k * COMPSIZE;
-      cc = c + (m - i)     * COMPSIZE;
+    if (m & (GEMM_UNROLL_M - 1)) {
+      for (i = 1; i < GEMM_UNROLL_M; i *= 2){
+        if (m & i) {
+          aa = a + ((m & ~(i - 1)) - i) * k * COMPSIZE;
+          cc = c + ((m & ~(i - 1)) - i)     * COMPSIZE;
 
-      if (k - kk > 0) {
-        GEMM_KERNEL(i, GEMM_UNROLL_N, k - kk, dm1,
+          if (k - kk > 0) {
+            GEMM_KERNEL(i, GEMM_UNROLL_N, k - kk, dm1,
 #ifdef COMPLEX
-            ZERO,
+                ZERO,
 #endif
-            aa + i             * kk * COMPSIZE,
-            b  + GEMM_UNROLL_N * kk * COMPSIZE,
-            cc,
-            ldc);
-      }
-
-      solve(i, GEMM_UNROLL_N,
-          aa + (kk - i) * i             * COMPSIZE,
-          b  + (kk - i) * GEMM_UNROLL_N * COMPSIZE,
-          cc, ldc);
+                aa + i             * kk * COMPSIZE,
+                b  + GEMM_UNROLL_N * kk * COMPSIZE,
+                cc,
+                ldc);
+          }
 
-      kk -= i;
+          solve(i, GEMM_UNROLL_N,
+              aa + (kk - i) * i             * COMPSIZE,
+              b  + (kk - i) * GEMM_UNROLL_N * COMPSIZE,
+              cc, ldc);
 
+          kk -= i;
+        }
+      }
     }
 
-    int mod = i;
-    i = vl;
-    if (i <= m) {
-      aa = a + (m - mod - vl) * k * COMPSIZE;
-      cc = c + (m - mod - vl)     * COMPSIZE;
+    i = (m >> GEMM_UNROLL_M_SHIFT);
+    if (i > 0) {
+      aa = a + ((m & ~(GEMM_UNROLL_M - 1)) - GEMM_UNROLL_M) * k * COMPSIZE;
+      cc = c + ((m & ~(GEMM_UNROLL_M - 1)) - GEMM_UNROLL_M)     * COMPSIZE;
 
       do {
         if (k - kk > 0) {
-          GEMM_KERNEL(vl, GEMM_UNROLL_N, k - kk, dm1,
+          GEMM_KERNEL(GEMM_UNROLL_M, GEMM_UNROLL_N, k - kk, dm1,
 #ifdef COMPLEX
               ZERO,
 #endif
-              aa + vl * kk * COMPSIZE,
+              aa + GEMM_UNROLL_M * kk * COMPSIZE,
               b +  GEMM_UNROLL_N * kk * COMPSIZE,
               cc,
               ldc);
         }
 
-        solve(vl, GEMM_UNROLL_N,
-            aa + (kk - vl) * vl * COMPSIZE,
-            b  + (kk - vl) * GEMM_UNROLL_N * COMPSIZE,
+        solve(GEMM_UNROLL_M, GEMM_UNROLL_N,
+            aa + (kk - GEMM_UNROLL_M) * GEMM_UNROLL_M * COMPSIZE,
+            b  + (kk - GEMM_UNROLL_M) * GEMM_UNROLL_N * COMPSIZE,
             cc, ldc);
 
-        aa -= vl * k * COMPSIZE;
-        cc -= vl     * COMPSIZE;
-        kk -= vl;
-
-        i += vl;
-      } while (i <= m);
+        aa -= GEMM_UNROLL_M * k * COMPSIZE;
+        cc -= GEMM_UNROLL_M     * COMPSIZE;
+        kk -= GEMM_UNROLL_M;
+        i --;
+      } while (i > 0);
     }
 
-
     b += GEMM_UNROLL_N * k * COMPSIZE;
     c += GEMM_UNROLL_N * ldc * COMPSIZE;
     j --;
@@ -298,59 +319,59 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k,  FLOAT dummy1,
 
         kk = m + offset;
 
-        i = m % vl;
-        if (i) {
-          aa = a + (m - i) * k * COMPSIZE;
-          cc = c + (m - i)     * COMPSIZE;
+        if (m & (GEMM_UNROLL_M - 1)) {
+          for (i = 1; i < GEMM_UNROLL_M; i *= 2){
+            if (m & i) {
+              aa = a + ((m & ~(i - 1)) - i) * k * COMPSIZE;
+              cc = c + ((m & ~(i - 1)) - i)     * COMPSIZE;
 
-          if (k - kk > 0) {
-            GEMM_KERNEL(i, j, k - kk, dm1,
+              if (k - kk > 0) {
+                GEMM_KERNEL(i, j, k - kk, dm1,
 #ifdef COMPLEX
-                ZERO,
+                    ZERO,
 #endif
-                aa + i * kk * COMPSIZE,
-                b  + j * kk * COMPSIZE,
-                cc, ldc);
-          }
-
-          solve(i, j,
-              aa + (kk - i) * i * COMPSIZE,
-              b  + (kk - i) * j * COMPSIZE,
-              cc, ldc);
+                    aa + i * kk * COMPSIZE,
+                    b  + j * kk * COMPSIZE,
+                    cc, ldc);
+              }
 
-          kk -= i;
+              solve(i, j,
+                  aa + (kk - i) * i * COMPSIZE,
+                  b  + (kk - i) * j * COMPSIZE,
+                  cc, ldc);
 
+              kk -= i;
+            }
+          }
         }
 
-        int mod = i;
-        i = vl;
-        if (i <= m) {
-          aa = a + (m - mod - vl) * k * COMPSIZE;
-          cc = c + (m - mod - vl)     * COMPSIZE;
+        i = (m >> GEMM_UNROLL_M_SHIFT);
+        if (i > 0) {
+          aa = a + ((m & ~(GEMM_UNROLL_M - 1)) - GEMM_UNROLL_M) * k * COMPSIZE;
+          cc = c + ((m & ~(GEMM_UNROLL_M - 1)) - GEMM_UNROLL_M)     * COMPSIZE;
 
           do {
             if (k - kk > 0) {
-              GEMM_KERNEL(vl, j, k - kk, dm1,
+              GEMM_KERNEL(GEMM_UNROLL_M, j, k - kk, dm1,
 #ifdef COMPLEX
                   ZERO,
 #endif
-                  aa + vl * kk * COMPSIZE,
+                  aa + GEMM_UNROLL_M * kk * COMPSIZE,
                   b +  j             * kk * COMPSIZE,
                   cc,
                   ldc);
             }
 
-            solve(vl, j,
-                aa + (kk - vl) * vl * COMPSIZE,
-                b  + (kk - vl) * j             * COMPSIZE,
+            solve(GEMM_UNROLL_M, j,
+                aa + (kk - GEMM_UNROLL_M) * GEMM_UNROLL_M * COMPSIZE,
+                b  + (kk - GEMM_UNROLL_M) * j             * COMPSIZE,
                 cc, ldc);
 
-            aa -= vl * k * COMPSIZE;
-            cc -= vl     * COMPSIZE;
-            kk -= vl;
-
-            i += vl;
-          } while (i <= m);
+            aa -= GEMM_UNROLL_M * k * COMPSIZE;
+            cc -= GEMM_UNROLL_M     * COMPSIZE;
+            kk -= GEMM_UNROLL_M;
+            i --;
+          } while (i > 0);
         }
 
         b += j * k   * COMPSIZE;
diff --git a/kernel/riscv64/trsm_kernel_LT_rvv_v1.c b/kernel/riscv64/trsm_kernel_LT_rvv_v1.c
index da443cfba..07a2c68a5 100644
--- a/kernel/riscv64/trsm_kernel_LT_rvv_v1.c
+++ b/kernel/riscv64/trsm_kernel_LT_rvv_v1.c
@@ -71,6 +71,30 @@ static FLOAT dm1 = -1.;
 #define GEMM_KERNEL   GEMM_KERNEL_N
 #endif
 
+#if GEMM_DEFAULT_UNROLL_M == 1
+#define GEMM_UNROLL_M_SHIFT 0
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 2
+#define GEMM_UNROLL_M_SHIFT 1
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 4
+#define GEMM_UNROLL_M_SHIFT 2
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 6
+#define GEMM_UNROLL_M_SHIFT 2
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 8
+#define GEMM_UNROLL_M_SHIFT 3
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 16
+#define GEMM_UNROLL_M_SHIFT 4
+#endif
+
 #if GEMM_DEFAULT_UNROLL_N == 1
 #define GEMM_UNROLL_N_SHIFT 0
 #endif
@@ -91,7 +115,9 @@ static FLOAT dm1 = -1.;
 #define GEMM_UNROLL_N_SHIFT 4
 #endif
 
-// Optimizes the implementation in ../arm64/trsm_kernel_LT_sve.c
+// Packed A is tiled by GEMM_UNROLL_M (the GEMM micro-kernel / itcopy contract),
+// NOT by runtime VL: tiling by VSETVL_MAX only matches x280 and silently
+// corrupts on other targets (e.g. zvl128b, unroll 8). Only solve() vectorizes.
 
 #ifndef COMPLEX
 
@@ -213,10 +239,6 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k, FLOAT dummy1,
   BLASLONG  kk;
   BLASLONG i, j;
 
-  size_t vl = VSETVL_MAX;
-
-    //fprintf(stderr, "%s , %s, m = %4ld  n = %4ld  k = %4ld offset = %4ld\n", __FILE__, __FUNCTION__, m, n, k, offset); // Debug
-
   j = (n >> GEMM_UNROLL_N_SHIFT);
 
   while (j > 0) {
@@ -225,47 +247,51 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k, FLOAT dummy1,
     aa = a;
     cc = c;
 
-    i = vl;
+    i = (m >> GEMM_UNROLL_M_SHIFT);
 
-    while (i <= m) {
+    while (i > 0) {
 
       if (kk > 0) {
-        GEMM_KERNEL(vl, GEMM_UNROLL_N, kk, dm1,
+        GEMM_KERNEL(GEMM_UNROLL_M, GEMM_UNROLL_N, kk, dm1,
 #ifdef COMPLEX
             ZERO,
 #endif
             aa, b, cc, ldc);
       }
 
-      solve(vl, GEMM_UNROLL_N,
-          aa + kk * vl * COMPSIZE,
+      solve(GEMM_UNROLL_M, GEMM_UNROLL_N,
+          aa + kk * GEMM_UNROLL_M * COMPSIZE,
           b  + kk * GEMM_UNROLL_N * COMPSIZE,
           cc, ldc);
 
-      aa += vl * k * COMPSIZE;
-      cc += vl     * COMPSIZE;
-      kk += vl;
-      i += vl;
+      aa += GEMM_UNROLL_M * k * COMPSIZE;
+      cc += GEMM_UNROLL_M     * COMPSIZE;
+      kk += GEMM_UNROLL_M;
+      i --;
     }
 
-    i = m % vl;
-    if (i) {
-      if (kk > 0) {
-        GEMM_KERNEL(i, GEMM_UNROLL_N, kk, dm1,
+    if (m & (GEMM_UNROLL_M - 1)) {
+      i = (GEMM_UNROLL_M >> 1);
+      while (i > 0) {
+        if (m & i) {
+          if (kk > 0) {
+            GEMM_KERNEL(i, GEMM_UNROLL_N, kk, dm1,
 #ifdef COMPLEX
-            ZERO,
+                ZERO,
 #endif
-            aa, b, cc, ldc);
-      }
-      solve(i, GEMM_UNROLL_N,
-          aa + kk * i             * COMPSIZE,
-          b  + kk * GEMM_UNROLL_N * COMPSIZE,
-          cc, ldc);
-
-      aa += i * k * COMPSIZE;
-      cc += i     * COMPSIZE;
-      kk += i;
+                aa, b, cc, ldc);
+          }
+          solve(i, GEMM_UNROLL_N,
+              aa + kk * i             * COMPSIZE,
+              b  + kk * GEMM_UNROLL_N * COMPSIZE,
+              cc, ldc);
 
+          aa += i * k * COMPSIZE;
+          cc += i     * COMPSIZE;
+          kk += i;
+        }
+        i >>= 1;
+      }
     }
 
     b += GEMM_UNROLL_N * k   * COMPSIZE;
@@ -283,11 +309,11 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k, FLOAT dummy1,
         aa = a;
         cc = c;
 
-        i = vl;
+        i = (m >> GEMM_UNROLL_M_SHIFT);
 
-        while (i <= m) {
+        while (i > 0) {
           if (kk > 0) {
-            GEMM_KERNEL(vl, j, kk, dm1,
+            GEMM_KERNEL(GEMM_UNROLL_M, j, kk, dm1,
 #ifdef COMPLEX
                 ZERO,
 #endif
@@ -297,37 +323,41 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k, FLOAT dummy1,
                 ldc);
           }
 
-          solve(vl, j,
-              aa + kk * vl * COMPSIZE,
+          solve(GEMM_UNROLL_M, j,
+              aa + kk * GEMM_UNROLL_M * COMPSIZE,
               b  + kk * j             * COMPSIZE, cc, ldc);
 
-          aa += vl * k * COMPSIZE;
-          cc += vl     * COMPSIZE;
-          kk += vl;
-          i += vl;
+          aa += GEMM_UNROLL_M * k * COMPSIZE;
+          cc += GEMM_UNROLL_M     * COMPSIZE;
+          kk += GEMM_UNROLL_M;
+          i --;
         }
 
-        i = m % vl;
-        if (i) {
-          if (kk > 0) {
-            GEMM_KERNEL(i, j, kk, dm1,
+        if (m & (GEMM_UNROLL_M - 1)) {
+          i = (GEMM_UNROLL_M >> 1);
+          while (i > 0) {
+            if (m & i) {
+              if (kk > 0) {
+                GEMM_KERNEL(i, j, kk, dm1,
 #ifdef COMPLEX
-                ZERO,
+                    ZERO,
 #endif
-                aa,
-                b,
-                cc,
-                ldc);
+                    aa,
+                    b,
+                    cc,
+                    ldc);
+              }
+
+              solve(i, j,
+                  aa + kk * i * COMPSIZE,
+                  b  + kk * j * COMPSIZE, cc, ldc);
+
+              aa += i * k * COMPSIZE;
+              cc += i     * COMPSIZE;
+              kk += i;
+            }
+            i >>= 1;
           }
-
-          solve(i, j,
-              aa + kk * i * COMPSIZE,
-              b  + kk * j * COMPSIZE, cc, ldc);
-
-          aa += i * k * COMPSIZE;
-          cc += i     * COMPSIZE;
-          kk += i;
-
         }
 
         b += j * k   * COMPSIZE;
diff --git a/kernel/riscv64/trsm_kernel_RN_rvv_v1.c b/kernel/riscv64/trsm_kernel_RN_rvv_v1.c
index 32e481036..c8c61cef6 100644
--- a/kernel/riscv64/trsm_kernel_RN_rvv_v1.c
+++ b/kernel/riscv64/trsm_kernel_RN_rvv_v1.c
@@ -68,6 +68,30 @@ static FLOAT dm1 = -1.;
 #define GEMM_KERNEL   GEMM_KERNEL_N
 #endif
 
+#if GEMM_DEFAULT_UNROLL_M == 1
+#define GEMM_UNROLL_M_SHIFT 0
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 2
+#define GEMM_UNROLL_M_SHIFT 1
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 4
+#define GEMM_UNROLL_M_SHIFT 2
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 6
+#define GEMM_UNROLL_M_SHIFT 2
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 8
+#define GEMM_UNROLL_M_SHIFT 3
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 16
+#define GEMM_UNROLL_M_SHIFT 4
+#endif
+
 #if GEMM_DEFAULT_UNROLL_N == 1
 #define GEMM_UNROLL_N_SHIFT 0
 #endif
@@ -88,7 +112,9 @@ static FLOAT dm1 = -1.;
 #define GEMM_UNROLL_N_SHIFT 4
 #endif
 
-// Optimizes the implementation in ../arm64/trsm_kernel_RN_sve.c
+// Packed A is tiled by GEMM_UNROLL_M (the GEMM micro-kernel / itcopy contract),
+// NOT by runtime VL: tiling by VSETVL_MAX only matches x280 and silently
+// corrupts on other targets (e.g. zvl128b, unroll 8). Only solve() vectorizes.
 
 #ifndef COMPLEX
 
@@ -209,11 +235,6 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k, FLOAT dummy1,
   BLASLONG  kk;
   BLASLONG i, j;
 
-  size_t vl = VSETVL_MAX;
-
-    //fprintf(stderr, "%s , %s, m = %4ld  n = %4ld  k = %4ld offset = %4ld\n", __FILE__, __FUNCTION__, m, n, k, offset); // Debug
-
-
   j = (n >> GEMM_UNROLL_N_SHIFT);
   kk = -offset;
 
@@ -222,47 +243,50 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k, FLOAT dummy1,
     aa = a;
     cc = c;
 
-    i = vl;
+    i = (m >> GEMM_UNROLL_M_SHIFT);
 
-    if (i <= m) {
+    if (i > 0) {
       do {
-	if (kk > 0) {
-	  GEMM_KERNEL(vl, GEMM_UNROLL_N, kk, dm1,
+        if (kk > 0) {
+          GEMM_KERNEL(GEMM_UNROLL_M, GEMM_UNROLL_N, kk, dm1,
 #ifdef COMPLEX
-		      ZERO,
+              ZERO,
 #endif
-		      aa, b, cc, ldc);
-	}
-
-	solve(vl, GEMM_UNROLL_N,
-	      aa + kk * vl * COMPSIZE,
-	      b  + kk * GEMM_UNROLL_N * COMPSIZE,
-	      cc, ldc);
-
-	aa += vl * k * COMPSIZE;
-	cc += vl     * COMPSIZE;
-	i += vl;
-      } while (i <= m);
-    }
+              aa, b, cc, ldc);
+        }
 
+        solve(GEMM_UNROLL_M, GEMM_UNROLL_N,
+            aa + kk * GEMM_UNROLL_M * COMPSIZE,
+            b  + kk * GEMM_UNROLL_N * COMPSIZE,
+            cc, ldc);
+
+        aa += GEMM_UNROLL_M * k * COMPSIZE;
+        cc += GEMM_UNROLL_M     * COMPSIZE;
+        i --;
+      } while (i > 0);
+    }
 
-    i = m % vl;
-    if (i) {
-      if (kk > 0) {
-        GEMM_KERNEL(i, GEMM_UNROLL_N, kk, dm1,
+    if (m & (GEMM_UNROLL_M - 1)) {
+      i = (GEMM_UNROLL_M >> 1);
+      while (i > 0) {
+        if (m & i) {
+          if (kk > 0) {
+            GEMM_KERNEL(i, GEMM_UNROLL_N, kk, dm1,
 #ifdef COMPLEX
-            ZERO,
+                ZERO,
 #endif
-            aa, b, cc, ldc);
+                aa, b, cc, ldc);
+          }
+          solve(i, GEMM_UNROLL_N,
+              aa + kk * i             * COMPSIZE,
+              b  + kk * GEMM_UNROLL_N * COMPSIZE,
+              cc, ldc);
+
+          aa += i * k * COMPSIZE;
+          cc += i     * COMPSIZE;
+        }
+        i >>= 1;
       }
-      solve(i, GEMM_UNROLL_N,
-          aa + kk * i             * COMPSIZE,
-          b  + kk * GEMM_UNROLL_N * COMPSIZE,
-          cc, ldc);
-
-      aa += i * k * COMPSIZE;
-      cc += i     * COMPSIZE;
-
     }
 
     kk += GEMM_UNROLL_N;
@@ -277,57 +301,61 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k, FLOAT dummy1,
     while (j > 0) {
       if (n & j) {
 
-	aa = a;
-	cc = c;
+        aa = a;
+        cc = c;
 
-  i = vl;
+        i = (m >> GEMM_UNROLL_M_SHIFT);
 
-	while (i <= m) {
-	  if (kk > 0) {
-	    GEMM_KERNEL(vl, j, kk, dm1,
+        while (i > 0) {
+          if (kk > 0) {
+            GEMM_KERNEL(GEMM_UNROLL_M, j, kk, dm1,
 #ifdef COMPLEX
-			ZERO,
+                ZERO,
 #endif
-			aa,
-			b,
-			cc,
-			ldc);
-	  }
-
-	  solve(vl, j,
-		aa + kk * vl * COMPSIZE,
-		b  + kk * j             * COMPSIZE, cc, ldc);
-
-	  aa += vl * k * COMPSIZE;
-	  cc += vl     * COMPSIZE;
-	  i += vl;
-	}
-
-  i = m % vl;
-  if (i) {
-	      if (kk > 0) {
-		GEMM_KERNEL(i, j, kk, dm1,
+                aa,
+                b,
+                cc,
+                ldc);
+          }
+
+          solve(GEMM_UNROLL_M, j,
+              aa + kk * GEMM_UNROLL_M * COMPSIZE,
+              b  + kk * j             * COMPSIZE, cc, ldc);
+
+          aa += GEMM_UNROLL_M * k * COMPSIZE;
+          cc += GEMM_UNROLL_M     * COMPSIZE;
+          i --;
+        }
+
+        if (m & (GEMM_UNROLL_M - 1)) {
+          i = (GEMM_UNROLL_M >> 1);
+          while (i > 0) {
+            if (m & i) {
+              if (kk > 0) {
+                GEMM_KERNEL(i, j, kk, dm1,
 #ifdef COMPLEX
-			    ZERO,
+                    ZERO,
 #endif
-			    aa,
-			    b,
-			    cc,
-			    ldc);
-	      }
-
-	      solve(i, j,
-		    aa + kk * i * COMPSIZE,
-		    b  + kk * j * COMPSIZE, cc, ldc);
-
-	      aa += i * k * COMPSIZE;
-	      cc += i     * COMPSIZE;
-
-  }
+                    aa,
+                    b,
+                    cc,
+                    ldc);
+              }
+
+              solve(i, j,
+                  aa + kk * i * COMPSIZE,
+                  b  + kk * j * COMPSIZE, cc, ldc);
+
+              aa += i * k * COMPSIZE;
+              cc += i     * COMPSIZE;
+            }
+            i >>= 1;
+          }
+        }
 
-	b += j * k   * COMPSIZE;
-	c += j * ldc * COMPSIZE;
-	kk += j;
+        b += j * k   * COMPSIZE;
+        c += j * ldc * COMPSIZE;
+        kk += j;
       }
       j >>= 1;
     }
diff --git a/kernel/riscv64/trsm_kernel_RT_rvv_v1.c b/kernel/riscv64/trsm_kernel_RT_rvv_v1.c
index 81cc41818..dd0eb6a74 100644
--- a/kernel/riscv64/trsm_kernel_RT_rvv_v1.c
+++ b/kernel/riscv64/trsm_kernel_RT_rvv_v1.c
@@ -67,6 +67,30 @@ static FLOAT dm1 = -1.;
 #define GEMM_KERNEL   GEMM_KERNEL_N
 #endif
 
+#if GEMM_DEFAULT_UNROLL_M == 1
+#define GEMM_UNROLL_M_SHIFT 0
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 2
+#define GEMM_UNROLL_M_SHIFT 1
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 4
+#define GEMM_UNROLL_M_SHIFT 2
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 6
+#define GEMM_UNROLL_M_SHIFT 2
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 8
+#define GEMM_UNROLL_M_SHIFT 3
+#endif
+
+#if GEMM_DEFAULT_UNROLL_M == 16
+#define GEMM_UNROLL_M_SHIFT 4
+#endif
+
 #if GEMM_DEFAULT_UNROLL_N == 1
 #define GEMM_UNROLL_N_SHIFT 0
 #endif
@@ -87,7 +111,9 @@ static FLOAT dm1 = -1.;
 #define GEMM_UNROLL_N_SHIFT 4
 #endif
 
-// Optimizes the implementation in ../arm64/trsm_kernel_RT_sve.c
+// Packed A is tiled by GEMM_UNROLL_M (the GEMM micro-kernel / itcopy contract),
+// NOT by runtime VL: tiling by VSETVL_MAX only matches x280 and silently
+// corrupts on other targets (e.g. zvl128b, unroll 8). Only solve() vectorizes.
 
 #ifndef COMPLEX
 
@@ -215,10 +241,6 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k,  FLOAT dummy1,
   FLOAT *aa, *cc;
   BLASLONG  kk;
 
-  size_t vl = VSETVL_MAX;
-
-    //fprintf(stderr, "%s , %s, m = %4ld  n = %4ld  k = %4ld offset = %4ld\n", __FILE__, __FUNCTION__, m, n, k, offset); // Debug
-
   kk = n - offset;
   c += n * ldc * COMPSIZE;
   b += n * k   * COMPSIZE;
@@ -234,52 +256,58 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k,  FLOAT dummy1,
         c -= j * ldc* COMPSIZE;
         cc  = c;
 
-        i = vl;
-        if (i <= m) {
+        i = (m >> GEMM_UNROLL_M_SHIFT);
+        if (i > 0) {
 
           do {
             if (k - kk > 0) {
-              GEMM_KERNEL(vl, j, k - kk, dm1,
+              GEMM_KERNEL(GEMM_UNROLL_M, j, k - kk, dm1,
 #ifdef COMPLEX
                   ZERO,
 #endif
-                  aa + vl * kk * COMPSIZE,
+                  aa + GEMM_UNROLL_M * kk * COMPSIZE,
                   b  +  j            * kk * COMPSIZE,
                   cc,
                   ldc);
             }
 
-            solve(vl, j,
-                aa + (kk - j) * vl * COMPSIZE,
+            solve(GEMM_UNROLL_M, j,
+                aa + (kk - j) * GEMM_UNROLL_M * COMPSIZE,
                 b  + (kk - j) * j             * COMPSIZE,
                 cc, ldc);
 
-            aa += vl * k * COMPSIZE;
-            cc += vl     * COMPSIZE;
-            i += vl;
-          } while (i <= m);
+            aa += GEMM_UNROLL_M * k * COMPSIZE;
+            cc += GEMM_UNROLL_M     * COMPSIZE;
+            i --;
+          } while (i > 0);
         }
 
-        i = m % vl;
-        if (i) {
-          if (k - kk > 0) {
-            GEMM_KERNEL(i, j, k - kk, dm1,
+        if (m & (GEMM_UNROLL_M - 1)) {
+          i = (GEMM_UNROLL_M >> 1);
+          do {
+            if (m & i) {
+
+              if (k - kk > 0) {
+                GEMM_KERNEL(i, j, k - kk, dm1,
 #ifdef COMPLEX
-                ZERO,
+                    ZERO,
 #endif
-                aa + i * kk * COMPSIZE,
-                b  + j * kk * COMPSIZE,
-                cc, ldc);
-          }
+                    aa + i * kk * COMPSIZE,
+                    b  + j * kk * COMPSIZE,
+                    cc, ldc);
+              }
 
-          solve(i, j,
-              aa + (kk - j) * i * COMPSIZE,
-              b  + (kk - j) * j * COMPSIZE,
-              cc, ldc);
+              solve(i, j,
+                  aa + (kk - j) * i * COMPSIZE,
+                  b  + (kk - j) * j * COMPSIZE,
+                  cc, ldc);
 
-          aa += i * k * COMPSIZE;
-          cc += i     * COMPSIZE;
+              aa += i * k * COMPSIZE;
+              cc += i     * COMPSIZE;
 
+            }
+            i >>= 1;
+          } while (i > 0);
         }
         kk -= j;
       }
@@ -297,52 +325,56 @@ int CNAME(BLASLONG m, BLASLONG n, BLASLONG k,  FLOAT dummy1,
       c -= GEMM_UNROLL_N * ldc * COMPSIZE;
       cc  = c;
 
-      i = vl;
-      if (i <= m) {
-	do {
-	  if (k - kk > 0) {
-	    GEMM_KERNEL(vl, GEMM_UNROLL_N, k - kk, dm1,
+      i = (m >> GEMM_UNROLL_M_SHIFT);
+      if (i > 0) {
+        do {
+          if (k - kk > 0) {
+            GEMM_KERNEL(GEMM_UNROLL_M, GEMM_UNROLL_N, k - kk, dm1,
 #ifdef COMPLEX
-			ZERO,
+                ZERO,
 #endif
-			aa + vl * kk * COMPSIZE,
-			b  + GEMM_UNROLL_N * kk * COMPSIZE,
-			cc,
-			ldc);
-	  }
-
-	  solve(vl, GEMM_UNROLL_N,
-		aa + (kk - GEMM_UNROLL_N) * vl * COMPSIZE,
-		b  + (kk - GEMM_UNROLL_N) * GEMM_UNROLL_N * COMPSIZE,
-		cc, ldc);
-
-	  aa += vl * k * COMPSIZE;
-	  cc += vl     * COMPSIZE;
-	  i += vl;
-	} while (i <= m);
+                aa + GEMM_UNROLL_M * kk * COMPSIZE,
+                b  + GEMM_UNROLL_N * kk * COMPSIZE,
+                cc,
+                ldc);
+          }
+
+          solve(GEMM_UNROLL_M, GEMM_UNROLL_N,
+              aa + (kk - GEMM_UNROLL_N) * GEMM_UNROLL_M * COMPSIZE,
+              b  + (kk - GEMM_UNROLL_N) * GEMM_UNROLL_N * COMPSIZE,
+              cc, ldc);
+
+          aa += GEMM_UNROLL_M * k * COMPSIZE;
+          cc += GEMM_UNROLL_M     * COMPSIZE;
+          i --;
+        } while (i > 0);
       }
 
-      i = m % vl;
-      if (i) {
-	    if (k - kk > 0) {
-	      GEMM_KERNEL(i, GEMM_UNROLL_N, k - kk, dm1,
+      if (m & (GEMM_UNROLL_M - 1)) {
+        i = (GEMM_UNROLL_M >> 1);
+        do {
+          if (m & i) {
+            if (k - kk > 0) {
+              GEMM_KERNEL(i, GEMM_UNROLL_N, k - kk, dm1,
 #ifdef COMPLEX
-			  ZERO,
+                  ZERO,
 #endif
-			  aa + i             * kk * COMPSIZE,
-			  b  + GEMM_UNROLL_N * kk * COMPSIZE,
-			  cc,
-			  ldc);
-	    }
-
-	    solve(i, GEMM_UNROLL_N,
-		  aa + (kk - GEMM_UNROLL_N) * i             * COMPSIZE,
-		  b  + (kk - GEMM_UNROLL_N) * GEMM_UNROLL_N * COMPSIZE,
-		  cc, ldc);
+                  aa + i             * kk * COMPSIZE,
+                  b  + GEMM_UNROLL_N * kk * COMPSIZE,
+                  cc,
+                  ldc);
+            }
 
-	    aa += i * k * COMPSIZE;
-	    cc += i     * COMPSIZE;
+            solve(i, GEMM_UNROLL_N,
+                aa + (kk - GEMM_UNROLL_N) * i             * COMPSIZE,
+                b  + (kk - GEMM_UNROLL_N) * GEMM_UNROLL_N * COMPSIZE,
+                cc, ldc);
 
+            aa += i * k * COMPSIZE;
+            cc += i     * COMPSIZE;
+          }
+          i >>= 1;
+        } while (i > 0);
       }
 
       kk -= GEMM_UNROLL_N;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants